package com.gitee.wsl.ext.string

import com.gitee.wsl.ext.array.toBinaryString
import com.gitee.wsl.ext.base.BYTE_BITS
import com.gitee.wsl.ext.string.builder.appendEscapedUnicode
import com.gitee.wsl.io.Charset
import com.gitee.wsl.text.regex.sample.BINARY_LEAD_REGEX
import com.gitee.wsl.text.regex.sample.HEX_LEAD_REGEX
import com.gitee.wsl.text.regex.sample.HEX_RADIX
import com.gitee.wsl.text.regex.sample.OCTAL_RADIX


inline infix fun <reified T> String?.mapTo(callBack:(String)->T):T?{
    if(!this.isNullOrEmpty()) return callBack(this)
    return null
}

fun String.toByteArray() = this.encodeToByteArray()

fun String.toByteArray(charset: Charset) = charset.encode(this)

fun String.decodeAsciiToByteArray() = ByteArray(length) {
    get(it).code.toByte()
}

fun String.toHexString() = this.toByteArray().toHexString()

/** 二进制编解码 */
fun String.toBinaryString() = this.toByteArray().toBinaryString()

fun String.binary2Ascii() = binary2ByteArray().toString()

fun String.binary2ByteArray(reversed: Boolean = false) =
    replace(Regex.BINARY_LEAD_REGEX, "")
        .asIterable()
        .chunked(BYTE_BITS)
        .map { it.joinToString("").run { if (reversed) reversed() else this }.toInt(2).toByte() }
        .toByteArray()

fun String.binaryReverse(reversed: Boolean = false) =
    if (reversed) {
        replace(Regex.BINARY_LEAD_REGEX, "").asIterable().chunked(BYTE_BITS).joinToString("") {
            it.joinToString("").reversed()
        }
    } else {
        this
    }

/** unicode编解码 */
fun String.toUnicodeString() =
    fold(StringBuilder()) { acc, c ->
        acc.append("\\u").append(c.code.toString(HEX_RADIX).padStart(4, '0'))
    }.toString()

/**
 * unicode 转字符串
 */
fun String.unicodeToString():String {
    val string = StringBuilder()
    val hex= split("\\\\u")
    for (i in 1..<hex.size) {
        // 转换出每一个代码点
        val data = hex[i].toInt(HEX_RADIX)

        // 追加成string
        string.append(data.toChar())
    }
    return string.toString()
}

/** js hex 编解码 \x61 */
fun String.encodeToJsHexString() =
    toByteArray()
        .map { it.toUByte() }
        .fold(StringBuilder()) { acc, c ->
            acc.append("\\x").append(c.toInt().toString(HEX_RADIX).padStart(2, '0'))
        }
        .toString()

/** js hex 解码 \x61 */
fun String.decodeJsHexString(): String =
    if (contains("\\x")) {
        split("(?i)\\\\x".toRegex())
            .filterNot { it.isEmpty() }
            .map { it.toInt(HEX_RADIX).toByte() }
            .toByteArray()
            .toString()
    } else {
        error("wrong format")
    }

/** js octal 编解码 \141 */
fun String.decodeJsOctalString() =
    if (contains("\\")) {
        split("\\\\".toRegex())
            .filterNot { it.isEmpty() }
            .map { it.toInt(OCTAL_RADIX).toByte() }
            .toByteArray()
            .toString()
    } else {
        error("wrong format")
    }

/** js octal 编码 \141 */
fun String.encodeToJsOctalString() =
    toByteArray()
        .map { it.toUByte() }
        .fold(StringBuilder()) { acc, c ->
            acc.append("\\").append(c.toInt().toString(OCTAL_RADIX))
        }
        .toString()


fun String.decodeHexToByteArray() =
    stripAllSpace()
        .replace(Regex.HEX_LEAD_REGEX, "")
        .chunked(2)
        .map { it.toInt(HEX_RADIX).toByte() }
        .toByteArray()

/**
 * Convert string to DBC.
 *  转化为半角字符
 *
 * @return the DBC string
 */
fun String.toDBC(): String {
    if ( isEmpty()) return ""
    val chars = this.toCharArray()
    var i = 0
    val len = chars.size
    while (i < len) {
        when (chars[i].code) {
            12288 -> {
                chars[i] = ' '
            }
            in 65281..65374 -> {
                chars[i] = (chars[i].code - 65248).toChar()
            }
            else -> {
                chars[i] = chars[i]
            }
        }
        i++
    }
    return chars.concatToString()
}

/**
 * Convert string to SBC.
 *  转化为全角字符
 *
 * @return the SBC string
 */
fun String.toSBC(): String {
    if (isEmpty()) return ""
    val chars = toCharArray()
    var i = 0
    val len = chars.size
    while (i < len) {
        if (chars[i] == ' ') {
            chars[i] = 12288.toChar()
        } else if (chars[i].code in 33..126) {
            chars[i] = (chars[i].code + 65248).toChar()
        } else {
            chars[i] = chars[i]
        }
        i++
    }
    return chars.concatToString()
}

/**
 * 下划线命名转为驼峰命名
 * @param str 下划线命名格式字符串
 * @return 驼峰命名格式字符串
 */
fun String.underScoreCaseToCamelCase(): String {
    val str = this
    if (str.isEmpty()) return str
    if (!str.contains("_")) return str
    val builder = StringBuilder()
    val chars = str.toCharArray()
    var hitUnderScore = false
    builder.append(chars[0])
    var i = 1
    val len = chars.size
    while (i < len) {
        val c = chars[i]
        if (c == '_') {
            hitUnderScore = true
        } else {
            if (hitUnderScore) {
                builder.append(c.uppercaseChar())
                hitUnderScore = false
            } else {
                builder.append(c)
            }
        }
        i++
    }
    return builder.toString()
}

/**
 * 驼峰命名法转为下划线命名
 * @param str 驼峰命名格式字符串
 * @return 下划线命名格式字符串
 */
fun String.camelCaseToUnderScoreCase(): String {
    val str=this
    if (str.isEmpty()) return str
    val builder = StringBuilder()
    val chars = str.toCharArray()
    for (value in chars) {
        if (value.isUpperCase()) {
            builder.append("_").append(value.lowercaseChar())
        } else {
            builder.append(value)
        }
    }
    return builder.toString()
}

/**
 * Set the first letter of string upper.
 *
 *
 * @return the string with first letter upper.
 */
fun String.upperFirstLetter():String  {
    if (isEmpty()) return ""
    return if (!this[0].isLowerCase()) this else (this[0].code - 32).toChar()
        .toString() + substring(1)
}


/**
 * Set the first letter of string lower.
 *
 *
 * @return the string with first letter lower.
 */
fun String.lowerFirstLetter(): String{
    if (isEmpty()) return ""
    return if (!this[0].isUpperCase()) this else (this[0].code + 32).toChar()
        .toString() + substring(1)
}

/**
 * Converting special escaped symbols like newlines, tabs and unicode symbols to proper characters for decoding
 *
 * @param lineNo line number of a string
 * @return returning a string with converted escaped special symbols
 * @throws ParseException if unknown escaped symbols were used
 * @throws UnknownEscapeSymbolsException
 */
 fun String.convertSpecialCharacters(lineNo: Int = 0): String {
    val resultString = StringBuilder()
    var i = 0
    while (i < length) {
        val currentChar = get(i)
        var offset = 1
        if (currentChar == '\\' && i != lastIndex) {
            // Escaped
            val next = get(i + 1)
            offset++
            when (next) {
                't' -> resultString.append('\t')
                'b' -> resultString.append('\b')
                'r' -> resultString.append('\r')
                'n' -> resultString.append('\n')
                'f' -> resultString.append('\u000C')
                '\\' -> resultString.append('\\')
                '\'' -> resultString.append('\'')
                '"' -> resultString.append('"')
                SIMPLE_UNICODE_PREFIX, COMPLEX_UNICODE_PREFIX ->
                    offset += resultString.appendEscapedUnicode(this, next, i + 2, lineNo)

                else -> throw Exception("\\$next LineNo:$lineNo")
            }
        } else {
            resultString.append(currentChar)
        }
        i += offset
    }
    return resultString.toString()
}

internal const val COMPLEX_UNICODE_LENGTH = 8
internal const val COMPLEX_UNICODE_PREFIX = 'U'
internal const val SIMPLE_UNICODE_LENGTH = 4
internal const val SIMPLE_UNICODE_PREFIX = 'u'

inline fun <reified T : Enum<T>> String?.toEnum(defaultValue: T): T =
    if (this == null) defaultValue
    else try {
        enumValueOf(this)
    } catch (e: IllegalArgumentException) {
        defaultValue
    }